iT邦幫忙

2022 iThome 鐵人賽

0
Modern Web

從新開始學習p5.js畫出一片天系列 第 39

D39_網頁Canvas上傳存檔與錄影記錄的操作

  • 分享至 

  • xImage
  •  

網頁Canvas上傳存檔與錄影記錄的操作

今天來整理一下利用 form 元件 將canvas的圖像上傳至網站儲存

HTML

<head>
	<meta charset="utf-8" />
	<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
	<link rel="icon" href="multicore_logo.ico" sizes="32x32">
	<!-- keep the line below for OpenProcessing compatibility -->
	<script src="https://openprocessing.org/openprocessing_sketch.js"></script>
	<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.2/p5.js"></script>
	<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.2/addons/p5.sound.js"></script>
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>                                                                                                               
	<script src="mySketch.js"></script>
	<link rel="stylesheet" type="text/css" href="style.css">
</head>

<body>
    <div id="d1"></div>
	<button id="btn1" onclick="saveCavnas()">儲存</button>
	<button id="btn2" onclick="startRecord()">開始錄影</button>
	<button id="btn3" onclick="stopRercord()">停止錄影</button>
	<div id="d2"></div>

	<video src=""></video>

	<form id="fr1" name="fr1" action="upload.php" method="post" target="ff1">
		<input id="img" type="hidden" name="img"><br><br>
		<input id="fname" type="hidden" name="fname"><br><br>
	</form>
	<iframe src="" name="ff1" id="ff1"></iframe>
<body>

style.css

html, body {
  margin: 0;
  padding: 0;
}

#ff1 {
  border:0px; 
  display:none;
}

mySketch.js

let can;
function setup() {
	can = createCanvas(400, 400);
	can.id("can1");
	can.parent("d1");

	background(0);
	stroke(255, 255, 0);
	strokeWeight(4);
}

function draw() {
	if(mouseIsPressed){
		line(pmouseX, pmouseY, mouseX, mouseY);
	}
}

function saveCavnas(){
	let url = "upload.php"; 
	let imgurl = can.elt.toDataURL("image/png");
    let fname = "ABC.png";
    let postData = { img: imgurl, fname: fname };

	console.log(select("#fr1"));
	
	select("#fname").value(fname);
	select("#img").value(imgurl);
	select("#fr1").elt.submit();
    
    /*
	$.post(url, postData, (data, status) => {
	  select("#d2").html("Data: " + data + "\nStatus: " + status);
    });
    */
}

其中除了利用form的 input上傳,也可以用jQuery的 $.post 方法上傳

	$.post(url, postData, (data, status) => {
	  select("#d2").html("Data: " + data + "\nStatus: " + status);
    });

upload.php

<?php
    $fname = $_POST["fname"];
    $img = $_POST["img"];
    $img = substr(explode(";",$img)[1], 7);
    file_put_contents($fname, base64_decode($img));
    echo $img;
?>

其中 substr(explode(";",$img)[1], 7); 主要是用來取出資料本體

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAZAAAAGQCAYAAACAvzbMAAAAAXNSR0IArs4c6QAAIABJREFUeF7t3U3sHVX9x/EvLW5kUWtqNMZQq8QawFQI0USNSgMuTAx0Jw+mSFlpKRDjQyKIBBYqJjxUMZrS1qitroCFMdFQNIqJBimYNlGCaRsSFkaELmRli/n+b+//zj2/M3fOfOfpPL

其中 explode(";",$img)[1]
以 ";" 做為分隔符號,變成
[0] data:image/png
[1] base64,iVBORw0KGgoAAAANSUhEUg ...

再用 substr(explode(";",$img)[1], 7); 取得第7個字元以後的字串(第幾個字元,以從第0個字元算起),最後變成
iVBORw0KGgoAAAANSUhEUg ..

//-----------------------
另一個功能是 將 canvas 的繪圖錄影
mySketch.js

let can;
let videoStream;
let mediaRecorder;
let chunks = [];  //-- 影片暫存片段
var video;

function setup() {
	can = createCanvas(400, 400);
	can.id("can1");
	can.parent("d1");

	background(0);
	stroke(255, 255, 0);
	strokeWeight(4);

	video = select("video");
	videoStream = can.elt.captureStream(30);  //-- 取得 canvas的 captureStream,設定影格速率每秒 30 格
	mediaRecorder = new MediaRecorder(videoStream); //-- 建立 MediaRecorder 影音錄製器

	mediaRecorder.ondataavailable = function(e) {
		chunks.push(e.data); //-- 當接收到影像資料時,放入影片暫存片段中
	};

	mediaRecorder.onstop = function(e) {
		
		//-- Blob(Binary large Object)物件,用來暫存影片片段資料,並設定檔案格式及編碼
		let blob = new Blob(chunks, { 'type' : 'video/webm;codecs=vp9' });  
		chunks = [];

		const videoURL = URL.createObjectURL(blob); //-- 建立暫存影片片段路徑
        
        video.src = videoURL;  //-- 設定 video 來源路徑
		video.loop();  //-- 執行循環播放

		const a = document.createElement("a"); //-- 建立下載超連結
        a.href = videoURL;    //-- 設定超連結路徑
        a.download = "recording.webm";   //-- 下載檔案名稱
        a.click();  //-- 執行點擊動作
        //URL.revokeObjectURL(videoURL); //-- 釋放暫存影片片段路徑 
        
        //-- 利用FileReader以base64的格式,讀取blob的資料
        let reader = new window.FileReader();  
		reader.readAsDataURL(blob);
		reader.onloadend = function () {
			base64data = reader.result;
			console.log(base64data);

			let urlv = "upload_video.php"; 
			let fnamev = "ABC.webm";
			let postDatav = { videox: base64data, fname: fnamev };
            
			//-- 將錄影影片上傳至網站
			$.post(urlv, postDatav, (data, status) => {
				select("#d2").html("Data: " + data + "\nStatus: " + status);
			});
		}   
	};
}

function draw() {
  if(mouseIsPressed){
    line(pmouseX, pmouseY, mouseX, mouseY);
  }
}

function startRecord(){
	mediaRecorder.start();
	console.log("start recording...");
}


function stopRercord(){
	mediaRecorder.stop();
	console.log("stop recording");
}

upload_video.php

<?php
    $fname = $_POST["fname"];
    $videox = $_POST["videox"];
    $videox = substr(explode(";",$videox)[2], 7);
    file_put_contents($fname, base64_decode($videox));
    echo $videox;
?>

其中 substr(explode(";",$videox)[2], 7); 主要是用來取出資料本體

data:video/webm;codecs=vp9;base64,GkXfo59ChoEBQveBAULygQRC84EIQoKEd2VibUKHgQRChYECGFOAZwH/////////FUmpZpkq17GDD0JATYCGQ2hyb21lV0GGQ2hyb21lFlSua6uuqdeBAXPFh0Qk/Y6RUUmDgQFV7oEBhoVWX1ZQOOCMsIIBkLqCAZBTwIEBH0O2dQH/////////54EAoEJqoUEvgQAAAJAjAJ0BKpA

其中 explode(";",$videox)[2]
以 ";" 做為分隔符號,變成
[0] data:video/webm
[1] codecs=vp9
[2] base64,GkXfo59ChoEBQveBAULyg ...

再用 substr(explode(";",$videox)[2], 7); 取得第7個字元以後的字串(第幾個字元,以從第0個字元算起),最後變成
GkXfo59ChoEBQveBAULyg ...

//------------- 補充 ----------
如果遇到上傳時,檔案太大被擋下來的話,可以修改可上傳檔案大小的設定

1. 查詢php.ini的所在目錄 
打開終端機,輸入 php --ini

得到回傳內容
Configuration File (php.ini) Path: /opt/homebrew/etc/php/8.1
Loaded Configuration File:         /opt/homebrew/etc/php/8.1/php.ini
Scan for additional .ini files in: /opt/homebrew/etc/php/8.1/conf.d
Additional .ini files parsed:      /opt/homebrew/etc/php/8.1/conf.d/ext-opcache.ini

2. 打開Finder > 前往 > 前往檔案夾...
   輸入 /opt/homebrew/etc/php/8.1

3. 開啟 sublime,將 php.ini 拖拉至sublime中

4. 修改以下2個參數設定後儲存
post_max_size = 1024M (預設值是8M)
upload_max_filesize = 1024M (預設值是2M)

//-----------------------
另外上傳檔案及資料的方式,經測試結果,
建議主要還是用 form 的 input 及 $.post() 的方式比較穩定

p5.js 的 httpPost() 及 JS 的 XMLHttpRequest()
都會出現接收不到上傳資料的問題,或是無法儲存檔案,或是有儲存檔案但是檔案打不開的問題

//------ 利用超連結下載錄製的影片檔 ------------

let chunks = [];  //-- 影片暫存片段
mediaRecorder.ondataavailable = function(e) {
    chunks.push(e.data); //-- 當接收到影像資料時,放入影片暫存片段中
};

//-- Blob(Binary large Object)物件,用來暫存影片片段資料,並設定檔案格式及編碼
let blob = new Blob(chunks, { 'type' : 'video/webm;codecs=vp9,opus' });  

const videoURL = URL.createObjectURL(blob); //-- 建立暫存影片片段路徑
const a = document.createElement("a"); //-- 建立下載超連結
a.href = videoURL;    //-- 設定超連結路徑
a.download = "recording.webm";   //-- 下載檔案名稱
a.click();  //-- 執行點擊動作

相關發文
D38_FileUpload 檔案上傳及縮圖處理的操作
https://ithelp.ithome.com.tw/articles/10310098


上一篇
D38_FileUpload 檔案上傳及縮圖處理的操作
下一篇
D40_從新開始學習p5.js畫出一片天_發文清單
系列文
從新開始學習p5.js畫出一片天40
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言